﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Collections;
using System.Threading;
using System.Net;
using WebSocketSharp;

namespace BestClient
{
    public class BestClientManager : ClientProcessorCallBack, BestDataCallBack {

        private const string TAG = "BestClientManager";
        private const string PRO = "best";
        private const string TOKEN = "token";
        private const string PERSON_SNAPSHOT = "personSnapshot";
        private const string COUNT = "count";
        private const string COMMAND_SNAPSHOT = "commandSnapshot";
        private const string SNAPSHOT = "snapshot";
        private const string PROT = "prot";
        private const string ZONE = "zone";
        private const string DEF_VALUE = "0";
        private const string DEFAULT = "default";
        private const string DATA_BUS_URL = "databusUrl";
        private const string CMP_ID = "cmpId";
        private const string WEB_URL_FORMAT = "{0}?SN={1}&pro={2}";
        private const string PERSON = "person";
        private const string COMMAND = "command";
        private const string CODE_OK = "00000000";
        private const string REBOOT_STATUS = "rebootStatus";
        private const string BEST_VERSION = "bestVersion";
        private const string FILE_URL = "fileUrl";
        private const string DEVICE_ON_LINE = "deviceOnLine";
        private const int MAX_BUFFER_SIZE = 1024 * 1024;

        private string urlParas;
        private Dictionary<string, Dictionary<string, DataSync>> snapshotMap = 
            new Dictionary<string, Dictionary<string, DataSync>>();
        private string noticeUserSnapShot = "";
        private string noticeCommandSnapShot = "";
        private string personSize = SettingsProvider.getInstance().getPersonSyncSize();
        private string commandSize = SettingsProvider.getInstance().getCommandSyncSize();
        private string dataBusUrl;
        private ClientMessageInteract clientMessageInteract;
        private SettingsProvider settingInstance;
        private string clientToken;
        private string cmpId;
        private BestDataProcessor bestDataProcessor;
        private static Dictionary<string, MethodInfo> SID_MAPPER = new Dictionary<string, MethodInfo>();
        private string serverCommandSnapshot;

        private Timer timer;
        private const int SEND_HEARTBEAT_INTERVAL = 20000;

        private bool isConnect = false;


        public BestClientManager() {
            //super(service, resources);
            //Log.d(TAG, "BestClientManager is running");
            this.settingInstance = SettingsProvider.getInstance();
            settingInstance.set(REBOOT_STATUS, CODE_OK);
            loadMethods();
            this.bestDataProcessor = new FC1000BestImplementation(null, this);
            

            System.Threading.Thread ts = new System.Threading.Thread(
                new System.Threading.ThreadStart(Run));
            ts.Start();
            //Executors.defaultThreadFactory().newThread(this).start();
        }

        private bool loop = false;
        private void Run(){
            bool restarted = false;
            try{
                do {
                    //Log.d(TAG, this + " - Initializing...");
                    loop = initialize();
                    if (loop) {
                        //Log.d(TAG, this + " - Initialization success");
                        do {
                            doWork();
                            // TODO Use a better alternative to sleep
                            System.Threading.Thread.Sleep(getWaitInterval());
                        } while (loop);
                        //Log.d(TAG, this + " - Stopped");
                        cleanup();
                        //Log.d(TAG, this + " - Cleaned up");
                    } else {
                        //Log.e(TAG, this + " - Initialization failed");
                        // TODO Use a better alternative to sleep
                        System.Threading.Thread.Sleep(getWaitInterval());
                    }
                } while (restarted);
            }
            catch(Exception ex){
                Thread.CurrentThread.Interrupt();
            }
            //bool loop = initialize();
            //do{
            //    doWork();
            //    System.Threading.Thread.Sleep(getWaitInterval());
            //}while(loop);
            //cleanup();
        }

        private void loadMethods() {
            //for (const Method method : this.getClass().getDeclaredMethods()) {
            //    const BestAPI annotation = method.getAnnotation(BestAPI.class);
            //    if (annotation != null) {
            //        SID_MAPPER.put(annotation.serviceId(), method);
            //    }
            //}
            foreach (MethodInfo method in this.GetType().GetMethods()) {
                BestAPI annotation = method.GetCustomAttribute<BestAPI>();
                if (annotation != null) {
                    SID_MAPPER.Add(annotation.serviceId, method);                
                }
            }
        }

        //@Override
        protected void cleanup() {
            connect(dataBusUrl);
        }

        //@Override
        protected void doWork() {
            getWebSocketURL(urlParas);
        }

        //@Override
        protected bool initialize() {
            urlParas = string.Format(WEB_URL_FORMAT, SettingsProvider.getInstance().getHttpUrl(), MessageConverter.getDeviceSN(), PRO);
            return true;
        }

        //@Override
        protected int getWaitInterval() {
            return int.Parse(SettingsProvider.getInstance().getBestCheckInterval());
        }

        /**
         * connect with middleware
         *
         * @param wsUrl middleware url
         */
        private void connect(string wsUrl) {
            try {
                //Log.i(TAG, "onStart: " + wsUrl);
                
                Uri uri = new Uri(wsUrl);
                clientMessageInteract = new ClientMessageInteract(uri, null);
                clientMessageInteract.setClientProcessorCallBack(this);
                clientMessageInteract.Run();
            } catch (Exception e) {
                Console.WriteLine(e.StackTrace);
                //Log.e(TAG, "WebSocket fail：" + e);
            }
        }

        private void stop(){
            this.loop = false;
        }

        /**
         * get middleware url from server
         *
         * @param reaUrl server url
         */
        private void getWebSocketURL(string reaUrl) {
            
            HttpWebRequest httpGet = (HttpWebRequest)WebRequest.Create(reaUrl);
            
            //HttpClient httpClient = new DefaultHttpClient();
            //Log.d(TAG, "getWebSocketURL: ," + httpGet.getURI().tostring());
            try {
                HttpWebResponse httpResponse = (HttpWebResponse)httpGet.GetResponse();
                //Log.i(TAG, "getWebSocketURL: ," + httpResponse.getStatusLine().getStatusCode());
                if (httpResponse.StatusCode == HttpStatusCode.OK) {
                    
                    byte[] respData = new byte[MAX_BUFFER_SIZE];
                    httpResponse.GetResponseStream().Read(respData, 0, respData.Length);
                    httpResponse.Close();
                    
                    string response = System.Text.Encoding.UTF8.GetString(respData);

                    BestData bestData = MessageConverter.fromJson<BestData>(response);
                    if (string.Equals(BestConstant.CODE_OK,bestData.code)) {
                        dataBusUrl = bestData.getPayload(DATA_BUS_URL).ToString();
                        clientToken = bestData.getPayload(TOKEN).ToString();
                        cmpId = bestData.getPayload(CMP_ID).ToString();
                        Console.WriteLine("DZ_getWebSocketURL:clientToken =" + clientToken);
                        Console.WriteLine("DZ_getWebSocketURL: cmpId=" + cmpId);
                        settingInstance.set(TOKEN, clientToken);
                        stop();
                    } else {
                        Console.WriteLine("DZ_getWebSocketURL: Message" + bestData.message);
                        Thread.Sleep(200);
                        getWebSocketURL(urlParas);
                    }
                }
            } 
            catch (Exception e) {
                Console.WriteLine(string.Format("Error:{0},Track:{1}",e.Message,e.StackTrace));
                try {
                    Thread.Sleep(20000);
                    getWebSocketURL(urlParas);
                } catch (Exception ie) {
                    Thread.CurrentThread.Interrupt();
                    Console.WriteLine(e.StackTrace);
                }

            }
        }

        //@Override
        public void afterOpen(ClientMessageInteract client) {
            isConnect = true;
            Console.WriteLine("DZ_afterOpen");
            settingInstance.set(DEVICE_ON_LINE, true);

            TimerCallback tc = new TimerCallback(keepAlive);
            timer = new System.Threading.Timer(tc,null, SEND_HEARTBEAT_INTERVAL,SEND_HEARTBEAT_INTERVAL);
            
           
            
            //timer.schedule(task, SEND_HEARTBEAT_INTERVAL, SEND_HEARTBEAT_INTERVAL);
            clientMessageInteract.Send(MessageConverter.registerData(new Context(), "best", "0.0.5"));
        }

        private void cancelTimer() {
            if (timer != null) {
                //timer.cancel();
                //timer = null;
                timer.Dispose();
            }
        }

        private void keepAlive(object obj) {
                    if (clientMessageInteract != null) {
                        
                        WebSocket webSocket = clientMessageInteract.getConnection();
                        //FramedataImpl1 framedataImpl1 = new FramedataImpl1(Framedata.Opcode.PING);
                        //framedataImpl1.setFin(true);
                        //webSocket.sendFrame(framedataImpl1);

                        webSocket.Ping();
                        
                    }
                }

        //@Override
        public void afterReceiveMsg(MessageEventArgs e)
        {
            string msg = e.Data;
            Console.WriteLine("DZ_" + msg);
            BestData bestData = MessageConverter.fromJson<BestData>(msg);
            if (string.Equals(bestData.sys, BestConstant.SYSTEM_ID_RESPONSE)) {
                string sid = string.Equals(bestData.code, BestConstant.CODE_OK) ? bestData.sid : ErrorCode.getError(bestData.code);
                Console.WriteLine("DZ_afterReceiveMsg: sid:__" + sid);
                bestData.sid = sid;
                if (!string.Equals(sid, BestConstant.RESEND_ERROR)) {
                    bool isSuccess = clientMessageInteract.dataBuffer.receiveDataAndDel(bestData.mid);
                    if (!isSuccess) {
                        Console.WriteLine("DZ_Response timeout,repeat message does not deal with");
                        return;
                    }
                    dispatchMessage(bestData);
                } else {
                    string message = clientMessageInteract.dataBuffer.getMessage(bestData.mid);
                    BestData bestDataResend = MessageConverter.fromJson<BestData>(message);
                    string newMid = (new Random()).NextDouble().ToString();
                    bestDataResend.mid = newMid;
                    if (string.Equals(bestData.sid, BestConstant.SERVICE_ID_REGISTER)) {
                        bestData.setPayloadParam("did", MessageConverter.generateDid(newMid));
                    }
                    clientMessageInteract.Send(message);
                }
            } else {
                dispatchMessage(bestData);
            }
        }

        //@Override
        public void afterClose(CloseEventArgs client)
        {
            Console.WriteLine("DZ_afterClose");
            settingInstance.set(DEVICE_ON_LINE, false);
            isConnect = false;
            cancelTimer();
            clientMessageInteract.dataBuffer.clearCache();
            try {
                do {
                    Thread.Sleep(5000);
                    connect(dataBusUrl);
                } while (!clientMessageInteract.connectBlocking());
            } catch (Exception e) {
                Thread.CurrentThread.Interrupt();
                Console.WriteLine(e.StackTrace);
            }
        }

        [BestAPI(serviceId = BestConstant.SERVICE_ID_REGISTER)]
        public void registerSucceed(BestData bestData) {
            Console.WriteLine("DZ_register Succeed");
            string version = (string) bestData.getPayloadResultKey("ver");
            string localVersion = (string) settingInstance.get(BEST_VERSION, "1.0.0");
            if (MessageConverter.compareVersion(version, localVersion) > 0) {
                settingInstance.set(BEST_VERSION, version);
            }
            clientMessageInteract.Send(MessageConverter.determineProtocol((new Context()),
                    (string) bestData.getPayloadResultKey(PROT), version));
        }

        [BestAPI(serviceId = BestConstant.SERVICE_ID_DETERMINE_PROTOCOL)]
        public void determineSucceed(BestData bestData) {
            Console.WriteLine("DZ_determine Succeed");
            string zone = (string) settingInstance.get(ZONE, "");
            try {
                string dataTag = FC1000BestImplementation.checkPersonDatabaseHasData() ? "1" : "0";
                clientMessageInteract.Send(MessageConverter.login(new Context(), cmpId, zone, dataTag));
            } catch (Exception e) {
                Console.WriteLine(e.StackTrace);
            }
        }

        [BestAPI(serviceId = BestConstant.SERVICE_ID_LOGIN)]
        public void loginSucceed(BestData bestData) {
            Console.WriteLine("DZ_login Succeed");
            //send reboot status
            string rebootStatus = (string) settingInstance.get(REBOOT_STATUS, BestConstant.DEVICE_OTHER_ERROR);
            if (rebootStatus.equals(BestConstant.CODE_OK)) {
                clientMessageInteract.Send(MessageConverter.uploadReceiveResult(getService(), BestConstant.SERVICE_ID_COMMAND_REBOOT_DEVICE, BestConstant.CODE_OK));
                settingInstance.Remove(REBOOT_STATUS);
            }
            settingInstance.set(ZONE, bestData.getPayloadResultKey(ZONE));
            Dictionary<string, string> map = new Dictionary<string, string>();
            map.Add(DEFAULT, "30");
            map.Add(BestConstant.SERVICE_ID_LOGIN, "20");
            clientMessageInteract.send(MessageConverter.report(getService(), map));
        }

        private Context getService(){
            return new Context();
        }

        [BestAPI(serviceId = BestConstant.SERVICE_ID_SUBSCRIBE)]
        public void subscribeSucceed(BestData bestData) {
            Console.WriteLine("DZ_subscribe Succeed");
            string personLocalSnapshot = (string) settingInstance.get(PERSON_SNAPSHOT, DEF_VALUE);
            clientMessageInteract.send(MessageConverter.uploadPersonSnapshot(getService(), personLocalSnapshot));
            string commandLocalSnapshot = (string) settingInstance.get(COMMAND_SNAPSHOT, DEF_VALUE);
            clientMessageInteract.send(MessageConverter.uploadCommandSnapshot(getService(), commandLocalSnapshot));
        }

        [BestAPI(serviceId = BestConstant.SERVICE_ID_UPLOAD_PERSONAL_SNAPSHOT)]
        public void uploadPersonSnapshotSucceed(BestData bestData) {
            Console.WriteLine("DZ_upload personal snapshot succeed");
            string serverPersonSnapshot = (string) bestData.getPayloadResultKey(SNAPSHOT);
            sendSyncRequest(serverPersonSnapshot, PERSON, PERSON_SNAPSHOT, personSize, BestConstant.SERVICE_ID_PERSON_SYNCHRONIZE);
        }

        [BestAPI(serviceId = BestConstant.SERVICE_ID_UPLOAD_COMMAND_SNAPSHOT)]
        public void uploadCommandSnapshotSucceed(BestData bestData) {
            Console.WriteLine("DZ_upload command snapshot succeed");
            serverCommandSnapshot = (string) bestData.getPayloadResultKey(SNAPSHOT);
            sendSyncRequest(serverCommandSnapshot, COMMAND, COMMAND_SNAPSHOT, commandSize, BestConstant.SERVICE_ID_COMMAND_SYNCHRONIZE);
        }

        [BestAPI(serviceId = BestConstant.SERVICE_ID_COMMAND_SYNC_NOTICE)]
        public void syncCommandNotice(BestData bestData) {
            Console.WriteLine("DZ_sync notice succeed");
            syncNoticeResponse(bestData, COMMAND, COMMAND_SNAPSHOT, noticeCommandSnapShot, commandSize, BestConstant.SERVICE_ID_COMMAND_SYNCHRONIZE);
        }

        [BestAPI(serviceId = BestConstant.SERVICE_ID_PERSON_SYNC_NOTICE)]
        public void syncPersonNotice(BestData bestData) {
            Console.WriteLine("DZ_sync notice succeed");
            syncNoticeResponse(bestData, PERSON, PERSON_SNAPSHOT, noticeUserSnapShot, personSize, BestConstant.SERVICE_ID_PERSON_SYNCHRONIZE);
        }

        [BestAPI(serviceId = BestConstant.SERVICE_ID_COMMAND_SYNCHRONIZE)]
        public void synCommandDataSucceed(BestData bestData) {
            Console.WriteLine("DZ_sync command data snapshot succeed");
            List<string> messageList = (List<string>) bestData.getPayloadResultKey("messages");
            foreach (string s in messageList) {
                Console.WriteLine("DZ_synCommandDataSucceed: " + s);
                BestData commandData = MessageConverter.fromJson<BestData>(s);
                if (commandData.sid.equals(BestConstant.SERVICE_ID_COMMAND_REBOOT_DEVICE)
                        || commandData.sid.equals(BestConstant.SERVICE_ID_COMMAND_CAMERA_INFORMATION)) {
                    string localSnapshot = (string) settingInstance.get(COMMAND_SNAPSHOT, DEF_VALUE);
                    Dictionary<string, DataSync> stringDataSyncMap = snapshotMap[COMMAND];
                    string newLocalSnapshot = stringDataSyncMap[localSnapshot].responseSnapshot;
                    Console.WriteLine("DZ_synCommandDataSucceed: newLocalSnapshot = " + newLocalSnapshot);
                    settingInstance.set(COMMAND_SNAPSHOT, newLocalSnapshot);
                }
                dispatchMessage(commandData);
            }
            syncResponse(bestData, noticeCommandSnapShot, COMMAND, COMMAND_SNAPSHOT, commandSize, BestConstant.SERVICE_ID_COMMAND_SYNCHRONIZE);
        }

        [BestAPI(serviceId = BestConstant.SERVICE_ID_REPORT)]
        public void reportSucceed(BestData bestData) {
            Console.WriteLine("DZ_report Succeed");
            clientMessageInteract.send(MessageConverter.subscribe(getService(), clientToken));
        }

        [BestAPI(serviceId = BestConstant.ACCESS_ERROR)]
        public void accessAgain(BestData bestData) {
            Console.WriteLine("DZ_request device center again");
            
            System.Threading.Thread ts = new System.Threading.Thread(
                new System.Threading.ThreadStart(Run));
            ts.Start();
        }

        [BestAPI(serviceId = BestConstant.WAIT_ERROR)]
        public void clientWait(BestData bestData) {
            Console.WriteLine("DZ_just wait ");
        }


        [BestAPI(serviceId = BestConstant.DELETE_ERROR)]
        public void clientDelete(BestData bestData) {
            Console.WriteLine("DZ_ delete person snapshot,person data,register again ");
            settingInstance.Remove(PERSON_SNAPSHOT);
            try {
                FC1000BestImplementation.clearPerson();
            } catch (Exception e) {
                Console.WriteLine(e.StackTrace);
            }
            clientMessageInteract.send(MessageConverter.registerData(getService(), "best", "0.0.5"));
        }

        [BestAPI(serviceId = BestConstant.SERVICE_ID_COMMAND_DEVICE_INFORMATION)]
        private string deviceInformationCommand(BestData bestData) {
            return MessageConverter.uploadDeviceInformation(getService());
        }

        [BestAPI(serviceId = BestConstant.UPDATE_SNAPSHOT_ERROR)]
        private void updateCommandSnpshot(BestData bestData) {
            Console.WriteLine("DZ_updateCommandSnpshot: update local command snapshot then sync");
            string minCommandSnapshot = (string) bestData.getPayloadParam(SNAPSHOT);
            settingInstance.set(COMMAND_SNAPSHOT, minCommandSnapshot);
            sendSyncRequest(serverCommandSnapshot, COMMAND, COMMAND_SNAPSHOT, commandSize, BestConstant.SERVICE_ID_COMMAND_SYNCHRONIZE);
        }

        [BestAPI(serviceId = BestConstant.SERVICE_ID_COMMAND_SYNC_ALL_PERSON)]
        private void syncAllPersonDate(BestData bestData) {
            Console.WriteLine("DZ_Sync all person data command : download file and update person snapshot");
            downloadFileSyncData(bestData);
        }

        [BestAPI(serviceId = BestConstant.UPDATE_SNAPSHOT_DATA_ERROR)]
        private void updateSnpshotAllDate(BestData bestData) {
            Console.WriteLine("DZ_Snapshot is too old : download file and update person snapshot");
            downloadFileSyncData(bestData);
        }

        [BestAPI(serviceId = BestConstant.NOT_FOUND_ERROR)]
        private void notFoundError(BestData bestData) {
            Console.WriteLine("DZ_notFoundError");
        }


        // TODO Makes no sense
        [BestAPI(serviceId = BestConstant.SERVICE_ID_COMMAND_DEVICE_SETTING)]
        private string deviceSettingCommand(BestData bestData) {
            // TODO: 2017/1/24 server set device information
            return null;
        }

        private void downloadFileSyncData(BestData bestData) {
            try {
                FC1000BestImplementation.clearPerson();
            } catch (Exception e) {
                Console.WriteLine(e.StackTrace);
            }
            string serverCurrentSnapshot = (string) bestData.getPayloadParam(SNAPSHOT);
            string decodeUrl = (string) bestData.getPayloadParam(FILE_URL);
            string count = (string) bestData.getPayloadParam(COUNT);
            if (int.Parse(count) > 0) {
                Console.WriteLine("DZ_updateSnapshotAllDate: decodeUrl=" + decodeUrl);
                //Daniel.Zhang reserved.
                //DownLoadTask task = new DownLoadTask(MessageConverter.decodeASCII(decodeUrl), settingInstance.getDownloadPersonPath(), bestDataProcessor);
                //task.execute();
            }
            settingInstance.set(PERSON_SNAPSHOT, serverCurrentSnapshot);
        }

        /**
         * dispatch sid ,include command and other sid
         *
         * @param bestData standard data from server
         */
        private void dispatchMessage(BestData bestData) {
            // If it can not find, it returns false
            MethodInfo method = null;
            if (SID_MAPPER.ContainsKey(bestData.sid))
            {
                method = SID_MAPPER[bestData.sid];
            }            
            if (method != null) {
                try {
                    //method.Invoke(this, bestData);
                    method.Invoke(this, new object[] { bestData });
                } catch (Exception ex){
                    Console.WriteLine(ex.StackTrace);
                }
            } else {
                // It means this sid belongs FC1000BestImplementation
                onDataProcessed(bestDataProcessor.processBestData(bestData));
                // Special case after sync person we need to send response immediately!!
                if (bestData.sid.equals(BestConstant.SERVICE_ID_PERSON_SYNCHRONIZE)) {
                    syncResponse(bestData, noticeUserSnapShot, PERSON,
                            PERSON_SNAPSHOT, personSize, BestConstant.SERVICE_ID_PERSON_SYNCHRONIZE);
                }
            }
        }

        public void onDataProcessed(string jsonData) {
            if (jsonData != null && isConnect) {
                clientMessageInteract.send(jsonData);
            }
        }

        /**
         * send person and command sync request
         *
         * @param key               include “person” or "command"
         * @param snapshotConstants include “personSnapshot” or "commandSnapshot"
         * @param size              size of data from server each time
         * @param sid               which sid ,include BestConstant.SERVICE_ID_PERSON_SYNCHRONIZE orClientSid.SERVICE_ID_COMMAND_SYNCHRONIZE
         */
        private void sendSyncRequest(string responseSnapshot, string key, string snapshotConstants, string size, string sid) {
            Console.WriteLine("DZ_responseSnapshot", responseSnapshot);
            string localSnapshot = (string) settingInstance.get(snapshotConstants, DEF_VALUE);
            if (int.Parse(localSnapshot) < int.Parse(responseSnapshot))
            {
                Dictionary<string, DataSync> dataSyncSnapshotMap = new Dictionary<string, DataSync>();
                dataSyncSnapshotMap.Add(localSnapshot, new DataSync(0, 0, responseSnapshot));
                snapshotMap.Add(key, dataSyncSnapshotMap);
                clientMessageInteract.send(MessageConverter.syncData(getService(), sid,
                        localSnapshot, responseSnapshot, DEF_VALUE, size));
            } else {
                settingInstance.set(snapshotConstants, responseSnapshot);
            }
        }

        /**
         * device receives the notice from server
         *
         * @param bestData       standard data
         * @param key            include “person” or "command"
         * @param noticeSnapShot include “noticeCommandSnapShot” or "noticePersonSnapshot"
         * @param size           size of data from server each time
         * @param sid            which sid ,include BestConstant.SERVICE_ID_PERSON_SYNCHRONIZE orClientSid.SERVICE_ID_COMMAND_SYNCHRONIZE
         */
        private void syncNoticeResponse(BestData bestData, string key, string snapshotConstants, string noticeSnapShot, string size, string sid) {
            string responseSnapshot = (string) bestData.getPayloadParam(SNAPSHOT);
            Console.WriteLine("DZ_syncNoticeResponse: snapshotConstants = " + snapshotConstants);
            string localSnapshot = (string) settingInstance.get(snapshotConstants, DEF_VALUE);
            Console.WriteLine("DZ_syncNoticeResponse: localSnapshot" + localSnapshot);
            if (snapshotMap.ContainsKey(key)) {
                Dictionary<string, DataSync> dataSyncSnapshotMap = snapshotMap[key];
                if (!dataSyncSnapshotMap.ContainsKey(localSnapshot)) {
                    dataSyncSnapshotMap.Add(localSnapshot, new DataSync(0, 0, responseSnapshot));
                    snapshotMap.Add(key, dataSyncSnapshotMap);
                    clientMessageInteract.send(MessageConverter.syncData(getService(), sid, localSnapshot, responseSnapshot, DEF_VALUE, size));
                } else {
                    Console.WriteLine("DZ_before sync finished ,not deal with .....");
                    noticeSnapShot = responseSnapshot;
                }
            } else {
                sendSyncRequest(responseSnapshot, key, snapshotConstants, size, sid);
            }
        }

        /**
         * when server send the data after device requests sync
         *
         * @param bestData          standard data
         * @param noticeSnapShot    include “noticeCommandSnapShot” or "noticePersonSnapshot"
         * @param key               include “person” or "command"
         * @param snapshotConstants include “personSnapshot” or "commandSnapshot"
         * @param size              size of data from server each time
         * @param sid               which sid ,include BestConstant.SERVICE_ID_PERSON_SYNCHRONIZE orClientSid.SERVICE_ID_COMMAND_SYNCHRONIZE
         */

        private void syncResponse(BestData bestData, string noticeSnapShot,
                                  string key, string snapshotConstants, string size, string sid) {
            string count = (string) bestData.getPayloadResultKey(COUNT);
            Console.WriteLine("DZ_syncResponse: count" + snapshotConstants + count);
            string localSnapshot = (string) settingInstance.get(snapshotConstants, DEF_VALUE);
            Dictionary<string, DataSync> dataSyncMap = snapshotMap[key];
            DataSync dataSync = dataSyncMap[localSnapshot];
            if (dataSync != null) {
                dataSync.setCount(int.Parse(count));
                Console.WriteLine("DZ_syncResponse: offsetCur" + dataSync.offsetCur);
                dataSync.offsetCur = dataSync.offsetCur + int.Parse(size);
                if (dataSync.offsetCur < dataSync.count) {
                    clientMessageInteract.send(MessageConverter.syncData(getService(), sid, localSnapshot,
                            dataSync.responseSnapshot, dataSync.offsetCur.ToString(), size));
                } else {
                    Dictionary<string, DataSync> stringDataSyncMap = snapshotMap[key];
                    string newLocalSnapshot = stringDataSyncMap[localSnapshot].responseSnapshot;
                    settingInstance.set(snapshotConstants, newLocalSnapshot);
                    Console.WriteLine("DZ_upData localSnapshot  ,is " + newLocalSnapshot);
                    if (!"".equals(noticeSnapShot)) {
                        Dictionary<string, DataSync> dataSyncSnapshotMap = new Dictionary<string, DataSync>();
                        dataSyncSnapshotMap.Add(newLocalSnapshot, new DataSync(0, 0, noticeSnapShot));
                        snapshotMap.Add(key, dataSyncSnapshotMap);
                        clientMessageInteract.send(MessageConverter.syncData(getService(), sid, newLocalSnapshot, noticeSnapShot, DEF_VALUE, size));
                        noticeSnapShot = "";
                    }

                }
            }
        }        
    }
}
